package server;

import java.util.*;
import server.tasks.MonitorListener;

public class ConnectionProvider implements MonitorListener {
	private ArrayList<NodeInfo> nodes;
	private int i = 0;
	private Object lock;
	private int attachedNodes;
	
	public ConnectionProvider(Object proxyLock){
		this.lock = proxyLock;
		this.attachedNodes = 0;
	}
	
	public NodeInfo nextConnection() {
		if(attachedNodes == 0 || nodes.size() == 0) {
			return null;
		} else if(nodes.get(0).getPriority() == 0) {
			if(i >= nodes.size())
				i = 0;
		
			return nodes.get(i++);			
		} else {
			Random r = new Random(System.currentTimeMillis());
			int orderToChoose = r.nextInt(1000);
			int order = 0;
			
			//to ma sens bo lista nodes jest przegladana w tym samym porzadku
			for(NodeInfo node : nodes) {
				if(node.isAttached()) {
					order += node.getPriority();
					
					if(orderToChoose < order)
						return node;
				}
			}
			
			return nodes.get(0); //na wszelki wypadek
		}
	}

	public void rebalance(Collection<MonitoringData> monitoringReport) {
		double sum = 0;
		double n = 1.0;
		
		synchronized(lock) {
			attachedNodes = 0;
			for(MonitoringData data : monitoringReport) {
				if(data.getNode().isAttached()) {
					sum += (1.0 / (double) data.getLoad());
					attachedNodes++;
				}
			}
						
			n = 1000 / sum;
		
			for(MonitoringData data : monitoringReport) {
				NodeInfo node = data.getNode();
				if(node.isAttached()) {
					int priority = (int) (n / (double) data.getLoad());
					node.setPriority(priority);
				} else {
					node.setPriority(0);
				}
			}				
		}
		
		Logger.logEvent("Priorytety serwerow zaktualizowane", Logger.LOG_INFO);
	}

	public void setNodes(ArrayList<NodeInfo> nodes) {
		this.nodes = nodes;	
	}
	
	public ArrayList<NodeInfo> getNodes() {
		return nodes;
	}

	@Override
	public void monitoringDone(Collection<MonitoringData> monitoringReport) {
		rebalance(monitoringReport);
	}

	@Override
	public void monitoringInterrupted() {
		return; 
	}

	public Collection<NodeInfo> getAttachedNodes() {
		LinkedList<NodeInfo> result = new LinkedList<NodeInfo>();
		
		if(attachedNodes == 0)
			return result;
		
		for(NodeInfo node : nodes){
			if(node.isAttached())
				result.add(node);
		}
		return result;
	}
	
	public Collection<NodeInfo> getDisattachedNodes() {
		LinkedList<NodeInfo> result = new LinkedList<NodeInfo>();

		if(attachedNodes == nodes.size())
			return result;
		
		for(NodeInfo node : nodes){
			if(!node.isAttached())
				result.add(node);
		}
		return result;
	}	
}
